package com.mysimpatico.sqlwrapper;

import com.mysimpatico.sqlwrapper.SqlWrapper.Type;

/**
 *
 * @author Gabriele Kahlout
 *
 *	Class for immutable object Column.
 *
 *  Type lengths is not supported in Java DB excepts for VARCHAR.
 */
public class Column {

    protected final String name;
    protected final Type type;
    protected final int typeLength;
    protected final Object def;
    protected final boolean unique;
    protected final boolean notNull;
    protected final String condition;



    public Column(final boolean unique, final String name, final int length, final SqlWrapper.Type type, final Object def,final boolean notNull, final String condition) {
        this.name = name;
        this.type = type;
        this.def = def;
        this.unique = unique;
        typeLength = length;
        this.notNull = notNull;
        this.condition = condition;
    }

    public Column(final boolean unique, final String name, final int length, final SqlWrapper.Type type, final Object def, final boolean notNull) {
        this(unique, name, length, type, def, notNull, null);
    }

    public Column(final boolean unique, final String name, final SqlWrapper.Type type) {
        this(unique, name, type, 0);
    }

    public Column(final String name, SqlWrapper.Type type, final int length) {
        this(false, name, type, length);
    }

    public Column(final String name, final SqlWrapper.Type type, final boolean def) {
        this(name, type, def, false);
    }

    public Column(final String name, final SqlWrapper.Type type, final boolean def, final boolean notNull) {
        this(name, 0, type, def);
    }

    public Column(final String name, final int length, final SqlWrapper.Type type, final Object def) {
        this(name, length, type, def, false);
    }

    public Column(final String name, final int length, final SqlWrapper.Type type, final Object def, final boolean notNull) {
        this(false, name, length, type, def, notNull);
    }

    public Column(final boolean unique, final String name, SqlWrapper.Type type, final int length) {
        this(unique, name, 0, type, null, false);
    }

    public Column(final boolean unique, final String name, Type type, final boolean notNull) {
        this(unique, name, 0, type, null, notNull);
    }

    public Column(final String name, SqlWrapper.Type type) {
        this(name, type, null);
    }

    public Column(final String name, final SqlWrapper.Type type, final Object def) {
        this(name, 0, type, def, false);
    }

    public static Column createIntColumn(final String name){
        return new Column(false,name,SqlWrapper.Type.INT);
    }

    public static Column createConstrainedForeignKey(final String name, final Table refTable, final Column refField, final boolean notNull, final Column checkVal) {
        return new ForeignKey(false, name, 0, refField.getType(), refField.getDef(), refTable, refField, notNull, name + '=' + checkVal);
    }

    public static Column createConstrainedColumn(final String name, final Type type, final String checkQuery, final Boolean exists) {
        return new Column(false, name, 0, type, null, false, (exists != null) ? (((exists) ? "NOT" : "") + " EXISTS( ") : "" + checkQuery + ")");
    }

    public static Column createForeignKey(final String name, final Table refTable, final Column refField, final boolean notNull) {
        return createConstrainedForeignKey(name, refTable, refField, notNull, null);
    }

    public static Column createForeignKey(final String name, SqlWrapper.Type type, final Object def, final Table refTable, final Column refField) {
        return new ForeignKey(false, name, refField.typeLength, type, def, refTable, refField, false, null);
    }

    public static Column createConstrainedReferencingColumn(final String name, final Table refTable, final Column refField, final boolean notNull, final Column checkVal, final char operator, final int val) {
        return new ForeignKey(false, name, refField.typeLength, refField.getType(), refField.getType(), refTable, refField, notNull, name + '=' + checkVal + operator + val);
    }

    public static Column createConstrainedColumn(final String name, final SqlWrapper.Type type, final boolean notNull, final String operator, final String val) {
        return new Column(false, name, 0, type, null, notNull, name + operator + "'" + val + "'");
    }

    public static Column createConstrainedColumn(final String name, final SqlWrapper.Type type, final Object def, final boolean notNull, final char operator, final int val) {
        return new Column(false, name, 0, type, def, notNull, name + operator + val);
    }

    public static Column createConstrainedColumn(final String name, final SqlWrapper.Type type, final boolean notNull, final char operator, final int val) {
        return createConstrainedColumn(name, type, null, notNull,operator, val);
    }

    public static Column createForeignKey(final boolean unique, final String name, final Table refTable, final Column refField, final boolean notNull) { //better rename
        return new ForeignKey(unique, name, refField.typeLength, refField.getType(), refField.getDef(), refTable, refField, notNull, null);
    }

    public static Column createForeignKey(final String name, final Table refTable, final Column refField) {
        return createForeignKey(name, refTable, refField, false);
    }

    public static Column createForeignKey(final String name, final Table refTable) {
        return createForeignKey(name, refTable, refTable.getIdColumn());
    }

    public static Column createForeignKey(final String name, final Table refTable, final boolean notNull) {
        return createForeignKey(name, refTable, refTable.getIdColumn(), notNull);
    }

    public static Column createConstrainedColumn(final String name, final int length, final SqlWrapper.Type type, final Object def, final boolean notNull, final String[] colNames, final SqlWrapper.Type[] colTypes, final String[] values, final char[] operators, final SqlWrapper.BinaryOperator[] conditions) {
        return new Column(false,name,length,type,def,notNull,SqlWrapper.joinWhereClauses(null, colNames, colTypes, values, operators, conditions));
    }

    public static Column createConstrainedColumn(final String name, final int length, final SqlWrapper.Type type, final Object def, final String[] values) {
        return new Column(false,name,length,type,def,true, SqlWrapper.joinWhereClauses(getColNames(name, values.length), new SqlWrapper.Type[values.length], values, false));
    }

    public static Column createConstrainedColumn(final String name, final int length, final SqlWrapper.Type type, final boolean notNull, final String[] values) {
        return new Column(false,name,length,type,null,notNull, SqlWrapper.joinWhereClauses(getColNames(name, values.length), getColTypes(type,values.length), values, false));
    }

    public static Column createConstrainedColumn(final String name, final int length, final SqlWrapper.Type type, final String[] values) {
        return new Column(false,name,length,type,null,false, SqlWrapper.joinWhereClauses(getColNames(name, values.length), getColTypes(type,values.length), values, false));
    }

    public static Column createConstrainedColumn(final String name, final int length, final SqlWrapper.Type type, final char[] values) {
        return new Column(false,name,length,type,null,false, SqlWrapper.joinWhereClauses(getColNames(name, values.length), getColTypes(type,values.length), values, false));
    }

    public static Column createConstrainedColumn(final String name, final int length, final SqlWrapper.Type type, final int[] vals) {
        return new Column(false,name,length,type,null,false, SqlWrapper.joinWhereClauses(getColNames(name, vals.length), getColTypes(type,vals.length), vals, false));
    }

    private static String[] getColNames(final String name, final int count) {
        final String[] colNames = new String[count];
        for (int i = 0; i < colNames.length; i++) {
            colNames[i] = name;
        }
        return colNames;
    }

    private static Type[] getColTypes(final Type type, final int count) {
        final SqlWrapper.Type[] colTypes = new SqlWrapper.Type[count];
        for (int i = 0; i < count; i++) {
            colTypes[i] = type;
        }
        return colTypes;
    }


    protected String getDefString() {
        if (def == null) {
            return "";
        }
        if (type == SqlWrapper.TEXT || type == SqlWrapper.CHAR || type == SqlWrapper.VARCHAR) {
            return " DEFAULT \"" + def.toString() + "\"";
        }
        return " DEFAULT " + def.toString();
    }

    protected String getLength() {
        if (typeLength != 0) {
            return "(" + typeLength + ")";
        } else {
            return "";
        }
    }

    protected String getNotNull() {
        if (notNull) {
            return " NOT NULL";
        } else {
            return "";
        }
    }

    protected String getUnique() {
        if (unique) {
            return " UNIQUE";
        } else {
            return "";
        }
    }

    protected String getCondition() {
        if (condition != null) {
            return " CHECK(" + condition + ")";
        }
        return "";
    }

    public String getName() {
        return name;
    }

    public String getName(Table table) {
        return (table == null) ? getName() : table.getName() + "." + name;
    }

    public SqlWrapper.Type getType() {
        return type;
    }

    Object getDef() {
        return def;
    }

    @Override
    public String toString() {
        return name + " " + type + getLength() + getDefString() + getUnique() + getNotNull() + getCondition();
    }

}